package org.extremengine.swing;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;

/**
 * Long time key press is not supported by java AWT, use this listener to implement
 * handler for it.
 * 实现原理：长按建的时候，AWT会收到多个KeyPressed和KeyReleased事件，一个KeyReleased时间和下一个KeyPressed间隔时间很小(1-2ms)。
 * 设定一个延迟时间，每次KeyReleased事件到达时，
 * 在另外一个线程中延迟判断按键释放：如果在延迟时间内有键再次按下，则视为同一按键；否则就是另外一次按键。
 * @author yuxing
 * 
 */
public abstract class LongPressKeyListener extends KeyAdapter {
	
	// If one key down, there will be an entry in the Key map.
	Map<Integer, Boolean> keyMap = new HashMap();
	
	Map<Integer, Boolean> isChecking = new HashMap();
	
	// 默认延迟判断时间
	public int DELAY_TIME = 10; // ms

	// Flag for key press down, "false" value to indicate KeyReleased event and 
	// anther handler thread will determine whether the key is really up.
	boolean isKeyDown;

//	boolean isChecking = false;
	
	KeyEvent e;

	private Runnable handler = new Runnable() {

		@Override
		public void run() {
			while (true) {
				try {
					synchronized (this) {
						this.wait();
					}

					Thread.sleep(DELAY_TIME);
//					isChecking = false;
					int key = e.getKeyCode();
					isChecking.put(key, false);
					if(!keyMap.containsKey(key) || keyMap.get(key) == false) { 
//					if (isKeyDown == false) {
						keyUp(e);
					}

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

	};

	private Thread thread = new Thread(handler);

	@Override
	public void keyPressed(KeyEvent e) {
		super.keyPressed(e);
		if (thread.isAlive() == false) {
			thread.start();
		}
		this.e = e;
		synchronized(handler) {
			int key = e.getKeyCode();
			if((!keyMap.containsKey(key) || keyMap.get(key) == false )
					&& (!isChecking.containsKey(key) || isChecking.get(key) == false)) {
//			if (isKeyDown == false && this.isChecking == false) {
				keyDown(e);
			}
//			isKeyDown = true;
			keyMap.put(key, true);
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		super.keyReleased(e);
		this.e = e;
		synchronized (handler) {
			int key = e.getKeyCode();
			keyMap.put(key, false);
			isChecking.put(key, true);
			handler.notifyAll();
		}
	}

	/**
	 * A key is held down.
	 * @param e
	 */
	public abstract void keyDown(KeyEvent e);
	
	/**
	 * A held down key is up.
	 * @param e
	 */
	public abstract void keyUp(KeyEvent e);
}
